home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / drgnsmth.cpt / Dragonsmith 1.1 / Base files / DFilePaths.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-11  |  13.7 KB  |  530 lines

  1. /*
  2.     DFilePaths.c
  3.     
  4.     A Dragon subspecies developed using THINK C 5.0
  5.  
  6.     Places a list of the full pathnames of the files dropped on it into the clipboard (desk scrap).
  7.     
  8.     Copyright ⌐ 1992 by Paul M. Hoffman
  9.     Send feedback to paul.hoffman@um.cc.umich.edu
  10.     
  11.     This code may be freely used, altered, and distributed in any way you want as long as:
  12.         1.    It is GIVEN away rather than sold;
  13.         2.    This statement and the above copyright notice are left intact.
  14.  
  15.     NOTE:    You should set the "Stationery aware" SIZE bit for this ╤ since it doesn't actually
  16.             do anything to the things dropped on it, we don't want the Finder to think it has to
  17.             make a copy of a stationery file before we get to see it!
  18.                 
  19.     Possible enhancements        ª    Add an option to create a file with the paths/names instead of
  20.                                 copying them to the clipboard.  Also append to a file?
  21. */
  22.  
  23. #include    "Dragon.h"
  24. #include    "FileUtils.h"
  25. #include    "MenuUtils.h"
  26. #include    "HandleUtils.h"
  27. #include    <Files.h>
  28. #include    <Types.h>
  29.  
  30. typedef struct {
  31.     char        left;
  32.     char        right;
  33. } QuotesPair;
  34.  
  35. typedef struct {
  36.     short        flags;
  37.     char            filler;
  38.     char            quotesStart;    // Initial setting of the quotes option
  39.     QuotesPair    quotes[4];    // There should be at least 4 quotes-pairs in the 'PthP' 128 resource
  40. } FPOptions;
  41.  
  42. class DFilePaths: Dragon {
  43.  
  44.     protected:
  45.         Handle            pathsHndl;        // Handle to the block where we accumulate the paths
  46.         long                blockLen;        // Length of the pathsHndl block
  47.         long                pathsLen;        // Length of the used portion of pathsHndl
  48.         long                threshold;        // Index of the byte szMaxPathLen bytes from the end of the block
  49.         long                limit;                // Index of the last byte in the block
  50.         char                quotesNum;        // Number of the quotes pair to use
  51.         QuotesPair        quotes;            // Quotes to use
  52.         Boolean            fullPaths;            // Return the full paths?
  53.         Boolean            showNesting;        // Give a hierarchical view of the docs?
  54.         Boolean            textFilesOnly;        // Process text files only?
  55.         FPOptions        **fpOptions;
  56.         MenuHandle        optionsMenu;
  57.         
  58.     public:
  59.                         DFilePaths (void);
  60.     
  61.     protected:
  62.         virtual void        BeginProcessing (void);
  63.         virtual void        EndProcessing (void);
  64.         virtual void        ProcessFile (void);
  65.         virtual void        ProcessDirectory (void);
  66.         virtual void        ProcessDocsInDirectory (short vRefNum, long dirID);
  67.         virtual Boolean    CustomFilterDoc (void);
  68.         virtual void        ReadPrefs (void);
  69.  
  70.         virtual void        SetUpMenus (void);
  71.         virtual void        InitOptionsMenu (void);
  72.         virtual void        SetQuotesOption (short optionItem);
  73.         virtual void        ToggleTextFilesOnly (void);
  74.         virtual void        ToggleFullPaths (void);
  75.         virtual void        ToggleShowNesting (void);
  76.         virtual void        DoMenu (long menuItemCode);
  77.         virtual void        DoOptionsMenu (short itemNum);
  78.         virtual void        DoAbout (void);
  79.         virtual void        AdjustMenusBusy (void);
  80.         virtual void        AdjustMenusIdle (void);
  81. };
  82.  
  83. #define    cRETURN    '\r'
  84. #define    cNIL            '\0'
  85. #define    cCOLON        ':'
  86. #define    cTAB        '\t'
  87.  
  88. #define    szMaxNameLength        32            // FSSpec file names can be up to 64 bytes, but we're
  89.                                         //     going to pretend MFS doesn't exist ╤ and file
  90.                                         //     names under HFS can only be 32 bytes
  91. #define    szMaxPathLen        1000        // One can reasonably expect the length of a file's path to be less than this
  92. #define    szExtraRoom            400            // Extra bytes to minimize # of handle resizings needed
  93. #define    szPathLenEstimate    70            // Estimated average path length
  94. #define    szGrowBy            szPathLenEstimate * 6    // Number of bytes to grow the pathsHndl block by
  95. #define    nSeveralDocs            10            // Make sure there's enough memory to process at least this many docs
  96.  
  97. enum {
  98.     mOptions = mEdit + 1
  99. };
  100.  
  101. // Items in the Options menu
  102. enum {
  103.     iFullPaths = 1,
  104.     iShowNesting,
  105.     iTextFilesOnly,
  106.     iLine2,
  107.     iNoQuotes,
  108.     iSingleQuotes,
  109.     iDoubleQuotes,
  110.     iAngleBrackets
  111.     // More quotes settings may follow here ╤ just add to 'MENU' 131 ( == mOptions) and 'PthP' 128
  112. };
  113.  
  114. enum {
  115.     prefFilePathOptions = prefDragonPrefs + 1
  116. };
  117.  
  118. // Possible values for the flags field in FPOptions
  119. enum {
  120.     maskShowNesting = 1,        // Least significant bit
  121.     maskTextFilesOnly = 2,
  122.     maskFullPaths = 4
  123. };
  124.  
  125. // Possible values for the quotesStart field in FPOptions (and hence this->quotesNum)
  126. enum {
  127.     qNone,
  128.     qSingle,
  129.     qDouble,
  130.     qAngle
  131.     // Other values may follow if you've added to the 'MENU' 131 and 'PthP' 128 resources
  132. };
  133.  
  134. // Possible error codes passed to StopProcessing
  135. enum {
  136.     eOverranPathsBlock = eLastDragonError + 1
  137. };
  138.  
  139. short GetFileName (FSSpec *fsspec, char *name);
  140. long GetFullPath (FSSpec *fsspec, char *path, OSErr *err);
  141. unsigned short ReverseCopyP2CStr (unsigned char *pas, char *c);
  142. void ReverseCStr (char *str);
  143. Boolean FSpIsFile (FSSpec *fss);
  144. Boolean FSpIsVolume (FSSpec *fss);
  145.  
  146. DFilePaths::DFilePaths (void)
  147. {
  148.     autoQuit = FALSE;
  149.     useCustomFilter = TRUE;
  150.     
  151.     // These DFilePaths-specific preferences settings will be overridden in ReadPrefs
  152.     quotesNum = qNone;
  153.     quotes.left = quotes.right = cNIL;
  154.     fullPaths = TRUE;
  155.     showNesting = FALSE;
  156.     textFilesOnly = TRUE;
  157. }
  158.  
  159. void DFilePaths::ReadPrefs (void)
  160. {
  161.     FPOptions    *fpop;
  162.     short        fpflags;
  163.  
  164.     inherited::ReadPrefs ();
  165.     
  166.     fpOptions = (FPOptions **) preferences->GetPrefResource (prefFilePathOptions);
  167.     if (fpOptions != NULL) {
  168.         HNoPurge ((Handle) fpOptions);
  169.         fpop = *fpOptions;
  170.         fpflags = fpop->flags;
  171.         fullPaths = ((fpflags & maskFullPaths) != FALSE);
  172.         showNesting = ((fpflags & maskShowNesting) != FALSE);
  173.         textFilesOnly = ((fpflags & maskTextFilesOnly) != FALSE);
  174.         if (textFilesOnly)
  175.             filesOnly = TRUE;
  176.         quotesNum = fpop->quotesStart;
  177.         quotes = fpop->quotes[quotesNum];
  178.     }
  179. }
  180.  
  181. void DFilePaths::SetUpMenus (void)
  182. {
  183.     // NOTE:    This method is called AFTER ReadPrefs (see Dragon::Start ╤ InitPrefs calls ReadPrefs)
  184.     
  185.     inherited::SetUpMenus ();
  186.     
  187.     optionsMenu = GetMenu (mOptions);
  188.     InsertMenu (optionsMenu, 0);
  189.     InitOptionsMenu ();
  190.     
  191.     DrawMenuBar ();
  192. }
  193.  
  194. void DFilePaths::InitOptionsMenu (void)
  195. {
  196.     // Initialize options menu (and set the options while we're at it)
  197.     CheckItem (optionsMenu, iFullPaths, fullPaths);
  198.     CheckItem (optionsMenu, iShowNesting, showNesting);
  199.     CheckItem (optionsMenu, iTextFilesOnly, textFilesOnly);
  200.     SetQuotesOption (iNoQuotes + quotesNum);
  201. }
  202.  
  203. void DFilePaths::SetQuotesOption (short optionItem)
  204. {
  205.     CheckOne (optionsMenu, iNoQuotes, iLastItem, optionItem);
  206.     quotesNum = optionItem - iNoQuotes;
  207.     if (fpOptions) {
  208.         (*fpOptions)->quotesStart = quotesNum;
  209.         quotes = (*fpOptions)->quotes[quotesNum];
  210.     }
  211. }
  212.  
  213. void DFilePaths::ToggleFullPaths (void)
  214. {
  215.     fullPaths = ToggleMenuItem (optionsMenu, iFullPaths);
  216.     if (fpOptions) {
  217.         if (fullPaths)
  218.             (*fpOptions)->flags |= maskFullPaths;
  219.         else
  220.             (*fpOptions)->flags &= ~maskFullPaths;
  221.     }
  222. }
  223.  
  224. void DFilePaths::ToggleShowNesting (void)
  225. {
  226.     showNesting = ToggleMenuItem (optionsMenu, iShowNesting);
  227.     if (fpOptions) {
  228.         if (showNesting)
  229.             (*fpOptions)->flags |= maskShowNesting;
  230.         else
  231.             (*fpOptions)->flags &= ~maskShowNesting;
  232.     }
  233. }
  234.  
  235. void DFilePaths::ToggleTextFilesOnly (void)
  236. {
  237.     textFilesOnly = ToggleMenuItem (optionsMenu, iTextFilesOnly);
  238.     if (textFilesOnly) {
  239.         if (fpOptions)
  240.             (*fpOptions)->flags |= maskTextFilesOnly;
  241.         filesOnly = TRUE;
  242.     } else if (fpOptions)
  243.         (*fpOptions)->flags &= ~maskTextFilesOnly;
  244. }
  245.  
  246. void DFilePaths::DoMenu (long menuItemCode)
  247. {
  248.     short    menuID, itemNum;
  249.     long        ticks;
  250.     
  251.     menuID = menuItemCode >> 16;
  252.     itemNum = menuItemCode & 0xFFFF;
  253.     
  254.     if (menuID == mOptions)
  255.         DoOptionsMenu (itemNum);
  256.     else
  257.         inherited::DoMenu (menuItemCode);
  258. }
  259.  
  260. void DFilePaths::DoOptionsMenu (short itemNum)
  261. {
  262.     switch (itemNum) {
  263.         case iFullPaths:
  264.             ToggleFullPaths ();
  265.             break;
  266.         case iShowNesting:
  267.             ToggleShowNesting ();
  268.             break;
  269.         case iTextFilesOnly:
  270.             ToggleTextFilesOnly ();
  271.             break;
  272.         case iNoQuotes:
  273.         case iSingleQuotes:
  274.         case iDoubleQuotes:
  275.         case iAngleBrackets:
  276.         default:
  277.             SetQuotesOption (itemNum);
  278.             break;
  279.     }
  280.     preferences->SavePrefResource (prefFilePathOptions);        // Save any changes that were made
  281. }
  282.  
  283. void DFilePaths::DoAbout (void)
  284. {
  285.     // I hope to eventually put something here╔
  286. }
  287.  
  288. void DFilePaths::AdjustMenusBusy (void)
  289. {
  290.     inherited::AdjustMenusBusy ();
  291.     DisableItem (optionsMenu, 0);    // Disable the Options menu
  292. }
  293.  
  294. void DFilePaths::AdjustMenusIdle (void)
  295. {
  296.     inherited::AdjustMenusIdle ();
  297.     EnableItem (optionsMenu, 0);        // Enable the options menu
  298. }
  299.  
  300. void DFilePaths::ProcessDirectory (void)
  301. {
  302.     if (!filesOnly)            // If we're not just supposed to process files,
  303.         ProcessFile ();        //    treat directories just like files
  304. }
  305.  
  306. void DFilePaths::ProcessDocsInDirectory (short vRefNum, long dirID)
  307. {
  308.     if (showNesting) {        // If we're supposed to return a hierarchical list,
  309.         curDirDepth++;    //    then pop up temporarily and
  310.         ProcessFile ();        //    put the directory name before its contents
  311.         curDirDepth--;        //    and push back down
  312.     }
  313.     inherited::ProcessDocsInDirectory (vRefNum, dirID);
  314. }
  315.  
  316. void DFilePaths::ProcessFile (void)
  317. {
  318.     register char    *p = *pathsHndl + pathsLen;
  319.     long            len;
  320.     OSErr        err;
  321.     register short    n;
  322.     
  323.     if (showNesting) {
  324.         for (n = curDirDepth; n < 0; n++)
  325.             *p++ = cTAB;
  326.         pathsLen -= curDirDepth;        // Subtract a negative to advance pathsLen in a positive direction
  327.     }
  328.     
  329.     // Stick a left quote in front, if appropriate
  330.     if (quotesNum != qNone) {
  331.         *p++ = quotes.left;
  332.         pathsLen++;
  333.     }
  334.     
  335.     // Now copy the file name or full path, depending on the setting of the "Full Paths" option
  336.     if (fullPaths) {
  337.         len = GetFullPath (curDocFSS, p, &err);
  338.         if (err != noErr) {                // Just skip this one if we had any problems
  339.             if (quotesNum != qNone)        // Back up a byte if we have written the opening quote character
  340.                 pathsLen--;
  341.             return;
  342.         }
  343.     } else
  344.         len = (long) GetFileName (curDocFSS, p);
  345.     p += len;
  346.     pathsLen += len;
  347.     
  348.     // Stick a right quote at the end, if appropriate
  349.     if (quotesNum != qNone) {
  350.         *p++ = quotes.right;
  351.         pathsLen++;
  352.     }
  353.     
  354.     // Add a return character and check to see if we're running out of room in the pathsHndl block
  355.     *p++ = cRETURN;
  356.     if (++pathsLen > threshold) {
  357.         if (pathsLen > limit)                                        // Have we overrun the end of the block?
  358.             Abort (eOverranPathsBlock);                            // If so, we should crash peacefully now
  359.         else {
  360.             blockLen += szGrowBy;                                // Enlarge the block to accomodate more paths
  361.             err = ResizeHandle (pathsHndl, blockLen);
  362.             if (err != noErr)
  363.                 StopProcessing (memFullErr);
  364.             else
  365.                 threshold = (limit = blockLen - 1) - szMaxPathLen;        // Reset limit and threshold accordingly
  366.         }
  367.     }
  368. }
  369.  
  370. void DFilePaths::BeginProcessing (void)
  371. {    
  372.     inherited::BeginProcessing ();
  373.  
  374.     // Allocate a block big enough to contain a reasonable number of paths
  375.     blockLen = szPathLenEstimate * nSeveralDocs + szMaxPathLen + szExtraRoom;
  376.     pathsLen = 0L;
  377.     pathsHndl = AnyHandle (blockLen);
  378.     if (pathsHndl == NULL)
  379.         StopProcessing (memFullErr);
  380.     
  381.     limit = blockLen - 1;
  382.     threshold = limit - szMaxPathLen;
  383. }
  384.  
  385. void DFilePaths::EndProcessing (void)
  386. {
  387.     OSErr    err;
  388.     
  389.     if (!abortProcessing) {
  390.         *(*pathsHndl + pathsLen - 1) = cNIL;        // Zero out the last byte (which was a cRETURN) of the paths
  391.         SetHandleSize (pathsHndl, pathsLen);
  392.         err = HandleToScrap (pathsHndl, 'TEXT');
  393.     }
  394.     
  395.     if (pathsHndl != NULL)
  396.         DisposHandle (pathsHndl);
  397.         
  398.     inherited::EndProcessing ();
  399. }
  400.  
  401. Boolean DFilePaths::CustomFilterDoc (void)
  402. {
  403.     if (textFilesOnly)
  404.         return curDocIsFile ? (curFileType == 'TEXT') : (dirDepthLimit < 0);
  405.     else
  406.         return TRUE;
  407. }
  408.  
  409. Dragon *CreateGDragon (void)
  410. {
  411.     return (Dragon *) new DFilePaths;
  412. }
  413.  
  414. short GetFileName (register FSSpec *fsspec, register char *name)
  415. {
  416.     short                    len, count;
  417.     register unsigned char        *p;
  418.     
  419.     p = (unsigned char *) fsspec->name;
  420.     count = len = *p++;
  421.     while (count-- != 0)
  422.         *name++ = *p++;
  423.     return len;
  424. }
  425.  
  426. long GetFullPath (FSSpec *fsspec, char *path, OSErr *err)
  427. {
  428.     short        volume = fsspec->vRefNum;
  429.     OSErr        fsErr = noErr;
  430.     CInfoPBRec    catinfo;
  431.     char            dirName[64];
  432.     long            retLen = 0L, len;
  433.     register char    *p = path;
  434.     
  435.     // All comments assume a  file whose full path is
  436.     //    HD:Fonts:Garamond
  437.     
  438.     // If fsspec doesn't designate a file (i.e., it's a volume or directory), we write a colon
  439.     if ( ! FSpIsFile (fsspec)) {
  440.         *p++ = cCOLON;
  441.         retLen++;
  442.     }
  443.     
  444.     // First, copy the file name backwards from a Pascal to a C string:
  445.     //    "\pGaramond"  becomes "dnomaraG"
  446.  
  447.     len = (long) ReverseCopyP2CStr (fsspec->name, p);
  448.     p += len;
  449.     retLen += len;
  450.     
  451.     if ( ! FSpIsVolume (fsspec)) {        // Don't do anything more if we've got a volume
  452.     
  453.         catinfo.dirInfo.ioVRefNum = volume;
  454.         catinfo.dirInfo.ioNamePtr = (StringPtr) dirName;
  455.         catinfo.dirInfo.ioDrParID = fsspec->parID;
  456.         
  457.         // Now copy the rest of the path, one level at a time, backwards:
  458.         //    "dnomaraG:stnoF:DH"
  459.         do {
  460.             catinfo.dirInfo.ioFDirIndex = -1;
  461.             catinfo.dirInfo.ioDrDirID = catinfo.dirInfo.ioDrParID;    // <= This is the key ╤ go from the
  462.                                                         //    current folder to its parent
  463.             fsErr = PBGetCatInfoSync (&catinfo);
  464.             if (fsErr == noErr) {
  465.                 *p++ = cCOLON;
  466.                 retLen += 1 + (len = ReverseCopyP2CStr ((StringPtr) dirName, p));
  467.                 p += len;
  468.             } else {
  469.                 *err = fsErr;
  470.                 return retLen;
  471.             }
  472.         } while (catinfo.dirInfo.ioDrDirID != fsRtDirID);
  473.     }
  474.     
  475.     // Finally, reverse the string and return its length and any error that might have occurred (ReverseCopyP2CStr has
  476.     //    already appended a null character, so we don't have to worry about it here)
  477.     ReverseCStr (path);
  478.     *err = fsErr;
  479.     return retLen;
  480. }
  481.  
  482. unsigned short ReverseCopyP2CStr (register unsigned char *pas, register char *c)
  483. {
  484.     register short    i, len = *pas++;
  485.     
  486.     for (i = len, c += len; i > 0; i--)
  487.         *--c = *pas++;
  488.     c += len;
  489.     *c = cNIL;
  490.     return len;
  491. }
  492.  
  493. void ReverseCStr (register char *str)
  494. {
  495.     register short        n = 0L, i;
  496.     register char        t, *p1, *p2;
  497.     
  498.     p1 = p2 = str;
  499.     while (*p2++)
  500.         ;
  501.     p2--;
  502.     n = (p2 - p1) / 2;
  503.     for (i = n; i > 0; i--) {
  504.         t = *--p2;
  505.         *p2 = *p1;
  506.         *p1++ = t;
  507.     }
  508. }
  509.  
  510. Boolean FSpIsFile (FSSpec *fss)
  511. {
  512.     CInfoPBRec    catinfo;
  513.     OSErr        err;
  514.     
  515.     if (FSpIsVolume (fss))
  516.         return FALSE;
  517.     catinfo.hFileInfo.ioNamePtr = fss->name;
  518.     catinfo.hFileInfo.ioVRefNum = fss->vRefNum;
  519.     catinfo.hFileInfo.ioFDirIndex = 0;
  520.     catinfo.hFileInfo.ioDirID = fss->parID;
  521.     err = PBGetCatInfoSync (&catinfo);
  522.     return ! (catinfo.hFileInfo.ioFlAttrib & ioDirMask);        // Bit 4 indicates it's a folder (or volume)
  523. }
  524.  
  525. Boolean FSpIsVolume (FSSpec *fss)
  526. {
  527.     return (fss->parID == fsRtParID);        // I guess this is what this constant is for ╔
  528. }
  529.  
  530.